import java.util.Iterator;
import java.util.Vector;

public class MomentProcessor 
{
	private Vector<Segment> segments;
    private boolean[][] binaryImage;
    private int width;
    private int height;

    public MomentProcessor(Vector<Segment> segs, boolean[][] pixels, int w, int h) 
    {
        segments = segs;
        width = w;
        height = h;
        binaryImage = pixels;
    }
    
    public void doMomentProcessing() 
    {
    	Log.d("MomentProcessor", "Starting computing of image moments!");
        for (Iterator<Segment> iterator = segments.iterator(); iterator.hasNext();) 
        {
            Segment seg = iterator.next();
            processOneSegment(seg);
        }
        Log.d("MomentProcessor", "Ending computing of image moments!");
    }        
    
	private void processOneSegment(Segment seg)
	{		
		int borders[] = seg.computeBordersAndDiameters();
		int centers[] = seg.computeCenters();
		int weightCenter[] = new int[2];
		
		Vector<SegmentPiece> pieces = seg.getPixels();
		
		int S = seg.getSize(); //surface
		int L = 0;
		
		double m00=0, m01=0,m10=0,m11=0,m20=0,m02=0,m21=0,
			   m12=0,m22=0, m30=0, m03=0;
		
		for (Iterator<SegmentPiece> iterator = pieces.iterator(); iterator.hasNext();) 
		{
			SegmentPiece sp = iterator.next();
            int x = sp.x;
            int y = sp.y;
            
            m00 += 1;
            m10 += x;
            m01 += y;
            m11 += x * y;
            m20 += x * x;
            m02 += y * y;
            m12 += x * y * y;
            m21 += x * x * y;
            m30 += x * x * x;
            m03 += y * y * y;
           
            if (x > 0 && x < binaryImage.length - 1 && y > 0 && y < binaryImage[0].length - 1) 
                if (!binaryImage[x - 1][y] || !binaryImage[x + 1][y] || !binaryImage[x][y - 1] || !binaryImage[x][y + 1])
                    L += 1;   
		}
            weightCenter[0] = (int) (m10 / m00);
            weightCenter[1] = (int) (m01 / m00);
            
            double rMin = width > height ? width : height; 
            double rMax = 1;
            
            for (Iterator<SegmentPiece> iterator = pieces.iterator(); iterator.hasNext();) 
    		{
    			SegmentPiece sp = iterator.next();
                int x = sp.x;
                int y = sp.y;
                
                if (x > 0 && x < binaryImage.length - 1 && y > 0 && y < binaryImage[0].length - 1)                
                    if (!binaryImage[x - 1][y] || !binaryImage[x + 1][y] || !binaryImage[x][y - 1] || !binaryImage[x][y + 1]) 
                    {                        
                        double dist = Math.sqrt(Math.pow(weightCenter[0] - x, 2) + Math.pow(weightCenter[1] - y, 2));
                        if (dist < rMin) 
                            rMin = dist;
                        
                        if (dist > rMax) 
                            rMax = dist;                        
                    }                
    		}
            
            int lMax = borders[1] - borders[0] > borders[3] - borders[2] ? borders[1] - borders[0] : borders[3] - borders[2];
           
            //count moments
            double picI = m10 / m00;
            double picJ = m01 / m00;
            
            //shape moments
            double W3 = L / (2 * Math.sqrt(Math.PI * S)) - 1;
            double W7_Lp1 = rMin / rMax;
            double W8_Lp2 = (double) lMax / (double) L;

            // geometric moments
            double M00 = m00;
            double M01 = m01 - (m01 / m00) * m00;
            double M10 = m10 - (m10 / m00) * m00;
            double M11 = m11 - m10 * m01 / m00;
            double M20 = m20 - m10 * m10 / m00;
            double M02 = m02 - m01 * m01 / m00;
            double M21 = m21 - 2 * m11 * picI - m20 * picJ + 2 * m01 * picI * picI;
            double M12 = m12 - 2 * m11 * picJ - m02 * picI + 2 * m10 * picJ * picJ;
            double M30 = m30 - 3 * m20 * picI + 2 * m10 * picI * picI;
            double M03 = m03 - 3 * m02 * picJ + 2 * m01 * picJ * picJ;
           
            double[] M = new double[11]; // first is not used
            M[1] = (M20 + M02) / Math.pow(m00, 2);
            M[2] = (Math.pow(M20 - M02, 2) + 4 * M11 * M11) / Math.pow(m00, 4);
            M[3] = (Math.pow(M30 - 3 * M12, 2) + Math.pow(3 * M21 - M03, 2)) / Math.pow(m00, 5);
            M[4] = (Math.pow(M30 + M12, 2) + Math.pow(M21 + M03, 2)) / Math.pow(m00, 5);
            M[5] = ((M30 - 3 * M12) * (M30 + M12) * (Math.pow(M30 + M12, 2) - 3 * Math.pow(M21 + M03, 2)) + (3 * M21 - M03)
                    * (M21 + M03) * (3 * Math.pow(M30 + M12, 2) - Math.pow(M21 + M03, 2)))
                    / Math.pow(m00, 10);
            M[6] = ((M20 - M02) * (Math.pow(M30 + M12, 2) - Math.pow(M21 + M03, 2)) + 4 * M11 * (M30 + M12) * (M21 + M03))
                    / Math.pow(m00, 7);
            M[7] = (M20 * M02 - M11 * M11) / Math.pow(m00, 4);
            M[8] = (M30 * M12 + M21 * M03 - M12 * M12 - M21 * M21) / Math.pow(m00, 5);
            M[9] = (M20 * (M21 * M03 - M12 * M12) + M02 * (M03 * M12 - M21 * M21) - M11 * (M30 * M03 - M21 * M12)) / Math.pow(m00, 7);
            M[10] = (Math.pow(M30 * M03 - M12 * M21, 2) - 4 * (M30 * M12 - M21 * M21) * (M03 * M21 - M12)) / Math.pow(m00, 10);
            
            // set computed moments to the segment
            seg.setMoments(new SegMomentsList(W3, W7_Lp1, W8_Lp2, M));
            seg.setL(L);
            seg.setS(S);
            seg.setWeights(weightCenter);            
	}
}
